﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OverStore.Configuration.Builder.Fluent;
using System.Data.SqlServerCe;
using System.IO;
using System.Reflection;
using OverStore.Runtime.Storage;
using OverStore.Configuration.Components;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace OverStore.TutorialAsTest
{
    [TestClass]
    public class FiveMinIntro
    {
        // Here short and simple example of using OverStore

        // Prepare database:
        // CREATE TABLE Animals(Animal_Id int not null primary key identity (1,1), Name nvarchar(150) not null, Is_Predator bit not null)

        // Create class stores data from Animals table:
        public class Animal
        {
            public int Id { get; set; }

            public string Name { get; set; }

            public bool IsPredator { get; set; }
        }

        [TestMethod]
        public void OverStoreFiveMinIntro()
        {
            var path = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath;
            // Configure OverStore to persists Animal objects in Animals table.
            var dbFileName = Path.Combine(Path.GetDirectoryName(path), "TestDb.sdf");
            var dbFileCopy = Path.Combine(Path.GetDirectoryName(path), "TestDb2.sdf");
            File.Copy(dbFileName, dbFileCopy);
            var connectionString = String.Format("Data Source = '{0}'", dbFileCopy);
            var configuration = OverStoreFluent.Configure()
                    .LogToConsole()
                    .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                    .MapClassToTable<Animal, int>("Animals") // class, identifier type and table name
                        .MapDbGeneratedKey(
                                "Animal_Id",                                        // Primary key column
                                animal => animal.Id,                                // Get primary key from Animal instance
                                (e, id) => e.Id = id,                               // Apply primary key to Animal instance
                                Component.IdentifierGenerator.Seq<Animal>(-1, -1))  // Sequentially generates temporary primary keys for new Animal objects
                        .MapScalarValue("Name", animal => animal.Name, (animal, name) => animal.Name = name)
                        .MapScalarValue("Is_Predator", animal => animal.IsPredator, (animal, isPredator) => animal.IsPredator = isPredator)
                        .ParameterlessReader(true) // Database query for reader without parameters
                            .ReadCommand()
                                .UseSqlCommand("select * from Animals")
                            .EndCommand()
                            .DelegateIdentifierProvider(r => r.GetInt32("Animal_Id"))
                        .EndReader()
                    .EndTableMapping()
                .End();

            // Add new Animal:
            using (var session = configuration.CreateSession())
            {
                var tiger = new Animal { Name = "Tiger", IsPredator = true };
                var elephant = new Animal { Name = "Elephant", IsPredator = false };
                session.Add(tiger);
                session.Add(elephant);
                session.SaveAll();            // Tiger is saved in database.
                Assert.AreEqual(1, tiger.Id); // Identifier is generated by database.
            }

            // Check Tiger is successfuly added:
            using (var session = configuration.CreateSession())
            {
                var tiger = session.GetByIdentifier<Animal, int>(1);
                Assert.AreEqual(1, tiger.Id);
                Assert.AreEqual("Tiger", tiger.Name);
                Assert.IsTrue(tiger.IsPredator);

                var elephant = session.GetByIdentifier<Animal, int>(2);
                Assert.AreEqual(2, elephant.Id);
                Assert.AreEqual("Elephant", elephant.Name);
                Assert.IsFalse(elephant.IsPredator);
            }

            // Upgrade Tiger to White Tiger
            using (var session = configuration.CreateSession())
            {
                var tiger = session.GetByIdentifier<Animal, int>(1);
                tiger.Id = 123;               // Try to crash OverStore by faking instance primary key.
                tiger.Name = "White Tiger!";
                session.SaveAll();

                var whiteTiger = session.GetByIdentifier<Animal, int>(1);
                Assert.AreSame(tiger, whiteTiger); // Once assigned, object identifier can't be changed.
            }

            // Check Tiger is upgraded to White Tiger:
            using (var session = configuration.CreateSession())
            {
                var tiger = session.GetByIdentifier<Animal, int>(1);
                Assert.AreEqual(1, tiger.Id);
                Assert.AreEqual("White Tiger!", tiger.Name);
                Assert.IsTrue(tiger.IsPredator);
            }

            using (var session = configuration.CreateSession())
            {
                // OverStore DOES NOT support LINQ to SQL, so fetch all animals to list.
                var animals = session.CreateReader<Animal>().ToList();
                // All two animals added before are here.
                Assert.AreEqual(2, animals.Count);
                var tiger = animals.FirstOrDefault(a => a.Name == "White Tiger!");  // It just LINQ to Collections.
                var elephant = animals.FirstOrDefault(a => a.Name == "Elephant");
                Assert.AreSame(tiger, session.GetByIdentifier<Animal, int>(1));    // OverStore has just one instance corresponing to table's row.
                Assert.AreSame(elephant, session.GetByIdentifier<Animal, int>(2)); // No matter how the instance is obtained.
            }
        }
    }
}
